home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
paint.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
79KB
|
3,715 lines
/*
* $Id: paint.c,v 0.91 1994/02/23 02:27:14 zhao Pre-Release $
*
*. This file is part of BIT shareware package. After the two weeks of
* free evaluation period, you are encouraged (required) to register
* your copy for a small registration fee, which is $35 for personal use
* and $50 for commercial, government and institutional use.
*
* Copyright(c) 1993, 1994 by T.C. Zhao.
* All rights reserved.
*
* Permission to use, copy, and distribute this software in its entirety
* for non-commercial purposes is hereby granted, provided that the
* above shareware and copyright notices and this permission notice
* appear in all copies and their documentation.
*
* This software may be modified for your own use, but modified versions
* may not be distributed without prior consent of the author.
*
* This software is provided "as is" without expressed or implied
* warranty of any kind.
*
*.
*
* Purpose:
* To support standard paint functions.
*
* All paint operators are based on a pen-plotter model, i.e., all
* objects are done via
*
* pen_init pen_down pen_move pen_up pen_motion pen_end pen_term
*
* Three more functions are used to facilitate the utilization of
* digital paint: pen_option, pen_outline and pen_redraw.
*
* This model appears to be adquate and clean for all operations that I
* want to implement.
*/
#if !defined(lint) && defined(F_ID)
char *id_pnt = "$Id: paint.c,v 0.91 1994/02/23 02:27:14 zhao Pre-Release $";
#endif
#define PAINT_DRIVER /* so paint.h loads static vars */
#include "bit.h"
#include "extern.h"
#include "paint.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/****************** Defines and Limits **************************/
#define MAX_LW 20 /* max. line width in pixels */
#define PAT_W 32 /* fill pattern width */
#define PAT_H 32 /* fill pattern height */
#define OP_COL 1 /* over_pup index used */
#define MAX_PNTS 2048 /* no. of pnts before flushing */
#define LW_BM_COL 258 /* internal use for */
/****************************************************************
* All paint tools operate on a "object" defined as Object_t, which
* could be thought as trace left behind if you move a pen
*****************************************************************/
typedef short Pnt_type; /* point type */
typedef struct
{
int n; /* total points so far */
int finished; /* if ready to be "painted" */
Pnt_type x[MAX_PNTS], y[MAX_PNTS];
}
Object_t;
static void
shift_object(Object_t * obj, Pnt_type dx, Pnt_type dy)
{
Pnt_type *x = obj->x, *y = obj->y, *xs = x + obj->n;
while (x < xs)
{
*x += dx;
*y += dy;
x++;
y++;
}
}
static Object_t cur_obj;
/* All pen command operates on the object defined by Object_t */
typedef void (*pen_cmd_t) (Object_t *);
/* Description of a particular paint tools */
typedef struct
{
pen_cmd_t pen_init; /* get ready */
pen_cmd_t pen_down; /* start tracing */
pen_cmd_t pen_move; /* motion while pen is down */
pen_cmd_t pen_up; /* end tracing */
pen_cmd_t pen_motion; /* motion while pen is up */
pen_cmd_t pen_end; /* ready to be painted */
pen_cmd_t pen_term; /* clean up before tool change */
pen_cmd_t outline; /* how to draw an outline */
pen_cmd_t redraw; /* transfer to canvas */
void (*option) (void); /* set tool specific options */
int spray; /* true if continous paint */
int closed; /* true if path is closed */
int builtin; /* true if redraw does meshing */
}
Draw_t;
/**** and paint tool as shown *************************************/
typedef struct
{
char *icons; /* filename or bitmap */
int bitmap; /* true if icon contains bitmaps */
Draw_t *(*make) (void); /* install this tool */
Draw_t *how; /* set after make */
}
PaintFunc;
/******************* Local varialbes ****************************/
static int cur_lw = 1; /* line width. 0 for none */
static int cur_ls; /* linw style */
static int total_ls; /* no. of built-ins. 0 solid */
static int cur_pat; /* pattern. 0 solid. */
static int ccol[4]; /* current pattern color */
static int lcol[4]; /* current outline color */
static Draw_t *pdraw; /* current drawing functions */
static pen_cmd_t pen_move; /* current tool tracing */
/******************** Local functions ****************************/
static void paint_leftmouse(long);
static void paint_middlemouse(long);
static void paint_keybd(int);
static int paint_init(IPTR, int);
static int get_current_point(Object_t *);
static void paint_finish(void);
static int init_line_style(void);
static FL_FORM *fpaint;
static FL_FORM *create_form_fpaint(void);
static void
set_line_attribute(void)
{
setlinestyle(cur_ls);
linewidth(cur_lw);
}
static void
reset_line_attribute(void)
{
setlinestyle(0);
linewidth(1);
}
/*********************************************************************
* Global entry point to paint function
**********************************************************************/
int
img_paint(IPTR im)
{
short val;
long dev;
/*
* unlike other bit functions, we create an image on the fly if no image
* is loaded
*/
if (!im || !im->ok)
{
imgptr = im = create_image("", T_RGBA, 640, 480, 0x00ffffff);
imgptr->io->display(imgptr, 0, 0);
}
/* currently, we only handle 24bits images */
if (!IS_RGBA(im))
{
val = img_convert_type(im, T_RGBA);
M_info("Paint", "Converting to RGB %sOK", val ? "not " : "");
im->io->display(im, 0, 0);
}
/*
* initialize paint system and install window manager's resize and
* reposition events
*/
paint_init(im, 0);
install_wm_handler(paint_init);
bit_show_form(create_form_fpaint(), FL_PLACE_POSITION, 0, "Paint");
/* Looping until ESC key or done button is pressed */
while ((dev = bit_qread(&val)) != KEYBD || val != 27)
{
switch (dev)
{
case LEFTMOUSE:
if (val)
paint_leftmouse(dev);
break;
case MENUBUTTON:
if (val && pdraw->option)
pdraw->option();
break;
case MIDDLEMOUSE:
if (val)
paint_middlemouse(dev);
break;
case KEYBD:
paint_keybd(val);
break;
case UPARROWKEY:
if (val)
paint_keybd('k');
break;
case DOWNARROWKEY:
if (val)
paint_keybd('j');
break;
case LEFTARROWKEY:
if (val)
paint_keybd('h');
break;
case RIGHTARROWKEY:
if (val)
paint_keybd('l');
break;
}
/*
* get_point reports current mouse location if it is different from
* where we were last time
*/
if (get_current_point(&cur_obj) && pdraw->pen_motion)
{
set_current_window(win_id);
switch_frame_buffer();
set_line_attribute();
pdraw->pen_motion(&cur_obj);
reset_line_attribute();
drawmode(NORMALDRAW);
}
}
/* close main control panel */
bit_hide_form(create_form_fpaint());
remove_wm_handler(paint_init);
/* flush last object */
if (pdraw->pen_term)
pdraw->pen_term(&cur_obj);
/* restore some graphics pipeline & bit default */
paint_finish();
return 0;
}
/*****************************************************************
* Under some paint tools, the standard way of handling the repaint
* is not adquate or need execessive buffering, the paint tool
* lets the driver know its traces by adding a "segment" which
* driver will take care of at the end of a paint job
****************************************************************/
static int nseg; /* no. ot segs so far */
static Rect_t segments[MAX_PNTS];
/* add one segment */
static void
add_segment(int x, int y, int dx, int dy)
{
Rect_t *s = segments + nseg++;
s->x = x;
s->y = y;
s->w = dx;
s->h = dy;
}
/********************************************************************
* Without background rendering support, any rasterization via
* framebuffer read may not work due to stuff on top of the image.
*******************************************************************/
static void ray_end(Object_t *);
static void
paint_rasterize(Object_t * obj, Rect_t * s)
{
int i;
/*
* ray is a special case in that although it is not closed, the only way
* to get it rasterize correctly is to use the complete bound
*/
if (pdraw->closed || pdraw->pen_end == ray_end)
{
s = spoly_bounds(obj->x, obj->y, obj->n);
inc_rect(s, 2 * cur_lw + 1, 2 * cur_lw + 1);
shift_rect(s, -cur_lw, -cur_lw);
fb_to_ras(imgptr, s);
}
else
{
if (obj->n > 30)
show_busy("");
/*
* have to do framebuffer read piecewise, otherwise, too many
* garbage falls into the bounds
*/
for (i = 0; i < obj->n - 1; i++)
{
s = spoly_bounds((obj->x) + i, (obj->y) + i, 2);
inc_rect(s, 2 * cur_lw + 1, 2 * cur_lw + 1);
shift_rect(s, -cur_lw, -cur_lw);
fb_to_ras(imgptr, s);
}
if (obj->n > 30)
end_busy();
}
}
/* how to handle it */
static void
handle_segments(Object_t * obj)
{
Rect_t *s = segments, *ss = s + nseg;
if (nseg)
{
M_info("HandleSeg", "n=%d", nseg);
while (--ss >= s)
fb_to_ras(imgptr, ss);
}
else
{
paint_rasterize(obj, s);
}
/* always reset */
nseg = 0;
}
/******************************************************************
* Find out where we are and if moved, report current location
*****************************************************************/
static int
get_current_point(Object_t * obj)
{
int x, y, n = obj->n;
static int ox, oy;
int moved;
get_mouse(&x, &y);
x -= win_xo;
y -= win_yo;
obj->x[n] = x;
obj->y[n] = y;
/* report mouse location only if moved */
if ((moved = (ox != x || oy != y)))
show_mouse_position(x, y);
ox = x;
oy = y;
return moved;
}
/*** Definations for paint tools as known by the driver *********/
static Draw_t *make_fpoly(void);
static Draw_t *make_pencil(void);
static Draw_t *make_line(void);
static Draw_t *make_circ(void);
static Draw_t *make_ray(void);
static Draw_t *make_erasor(void);
static Draw_t *make_brush(void);
static Draw_t *make_sqr(void);
static Draw_t *make_default(void);
static Draw_t *make_spray(void);
static Draw_t *make_obj3d(void);
static Draw_t *make_arc(void);
static Draw_t *make_fill(void);
static Draw_t *make_cut_paste(void);
static Draw_t *make_import(void);
static PaintFunc paintfunc[] =
{
{"pen.icon", 0, make_pencil},
{"brush.icon", 0, make_brush},
{"fill.icon", 0, make_fill},
{"spray.icon", 0, make_spray},
{"erasor.icon", 0, make_erasor},
{"cut.icon", 0, make_cut_paste},
{"obj3d.icon", 0, make_obj3d},
{line_bits, 1, make_line},
{fpoly_bits, 1, make_fpoly},
{circ_bits, 1, make_circ},
{arc_bits, 1, make_arc},
{rec_bits, 1, make_sqr},
{ray_bits, 1, make_ray}
};
static int npaintfunc = sizeof(paintfunc) / sizeof(paintfunc[0]);
static void
set_fill_pattern(void)
{
linewidth(cur_lw);
setlinestyle(cur_ls);
setpattern(cur_pat);
/*
* issue mesh command only if we know the draw command itself does not
* issue its own. Otherwise mess up the graphics pipeline badly
*/
if (!pdraw->builtin)
{
(!pdraw->closed ? bgnline :
(cur_pat != PAT_HOLLOW ? bgnpolygon : bgnclosedline)) ();
}
}
static void
reset_fill_pattern(void)
{
if (!pdraw->builtin)
{
(!pdraw->closed ? endline :
(cur_pat != PAT_HOLLOW ? endpolygon : endclosedline)) ();
}
setpattern(0);
setlinestyle(0);
linewidth(1);
}
static short svc[3]; /* original overlay color */
static oldm_report; /* copy of gloabl mouse report opt */
static IPTR saved; /* back up copy of image for undo */
static int
paint_init(IPTR im, int wme)
{
static int lxi, lyi;
if (wme > 0)
{
long od;
/*
* figure out how much image has moved as a result of window
* manager's resize/reposition
*/
shift_object(&cur_obj, im->xi - lxi, im->yi - lyi);
clear_over_pup();
/* get current active framebuffer */
set_current_window(win_id);
od = getdrawmode();
#if 1
switch_frame_buffer();
set_line_attribute();
color(OP_COL);
if (cur_obj.n > 0 && pdraw->redraw)
{
if (!pdraw->builtin)
bgnline();
pdraw->redraw(&cur_obj);
if (!pdraw->builtin)
endline();
}
set_line_attribute();
#else
draw_it(&cur_obj);
#endif
/*
* need to shift the backup copy as well as erasor refers to its
* location
*/
if (saved && saved->ok)
{
saved->xi = im->xi;
saved->yi = im->yi;
saved->xf = im->xf;
saved->yf = im->yf;
}
/* restore active framebuffer */
drawmode(od);
}
else
{
create_form_fpaint();
oldm_report = report_mouse;
report_mouse = 1;
saved = img_dup(im);
/*
* must initialize the lcol[3] to something that is not large than
* im->cmap->colors
*/
if (IS_CI(im))
{
lcol[3] = ccol[3] = 0;
lcol[0] = ccol[0] = im->cmap->ct[0][ccol[3]];
lcol[1] = ccol[1] = im->cmap->ct[1][ccol[3]];
lcol[2] = ccol[2] = im->cmap->ct[2][ccol[3]];
}
set_current_window(win_id);
/* initialize paint tool specific stuff */
if (pdraw && pdraw->pen_init)
pdraw->pen_init(&cur_obj);
fl_mapcolor(LW_BM_COL, lcol[0], lcol[1], lcol[2]);
switch_frame_buffer();
getmcolor(OP_COL, svc, svc + 1, svc + 2);
mapcolor(OP_COL, ccol[0], ccol[1], ccol[2]);
drawmode(NORMALDRAW);
/*
* although point sampled polygone looks better, can't use it as
* other parts of the program assumes oldpolygon
*/
glcompat(GLC_OLDPOLYGON, 1);
/*
* pay a little performance penelty to get free hand polygon
* rendered correctly on PI, Indgoes and maybe others
*/
concave(1);
total_ls = init_line_style();
cur_obj.n = cur_obj.finished = 0;
}
lxi = im->xi;
lyi = im->yi;
return 0;
}
/********************************************************************
* Finish up and restore all defaults
********************************************************************/
static void
paint_finish(void)
{
report_mouse = oldm_report;
op_mapcolor(OP_COL, svc[0], svc[1], svc[2]);
if (pdraw->pen_end)
pdraw->pen_end(&cur_obj);
drawmode(NORMALDRAW);
/*
* there are several places 1pixel wide rectangle might be used, must let
* GL do outline to be correct
*/
glcompat(GLC_OLDPOLYGON, 1);
/* restore all graphics pineline default */
concave(0);
linewidth(1);
setlinestyle(0);
setpattern(0);
clear_over_pup();
free_image(saved);
saved = 0;
rubber_finish();
}
static int changed; /* true if image's been altered */
/****************************************************************
* Replace saved copy with current image
****************************************************************/
static void
paint_update(void)
{
if (changed)
{
free_image(saved);
saved = img_dup(imgptr);
changed = 0;
}
}
/*****************************************************************
* Restore saved copy
*****************************************************************/
static void
paint_undo(void)
{
if (changed)
{
free_image(imgptr);
imgptr = img_dup(saved);
imgptr->io->display(imgptr, 0, 1);
changed = 0;
}
}
/***********************************************************************
* Actual rendering
***********************************************************************/
static void
draw_it(Object_t * obj)
{
int outline_only;
Color4(ccol);
set_fill_pattern();
smooth_line_on(imgptr, ccol, cur_lw);
pdraw->redraw(obj);
reset_fill_pattern();
smooth_line_off(imgptr, ccol);
/*
* draw outlines unless the area is not closed or current outline option
* is none, indicated by line width <= 0. Further if current pattern is
* none and area is closed, the outline IS the stuff we want, swap colors
*/
outline_only = (pdraw->closed && cur_pat == PAT_HOLLOW && pdraw->outline);
if (outline_only || (pdraw->outline && cur_lw > 0))
{
int *cc = outline_only ? ccol : lcol;
set_line_attribute(); /* need to go before smooth_line */
smooth_line_on(imgptr, cc, cur_lw);
if (!pdraw->builtin)
bgnclosedline();
pdraw->outline(obj);
if (!pdraw->builtin)
endclosedline();
reset_line_attribute();
smooth_line_off(imgptr, cc);
}
}
/********************************************************************
* Regenerate traces and optionally render it into core mem via
* framebuffer read
********************************************************************/
static void
paint_it(Object_t * obj)
{
if (obj->n && obj->finished)
{
set_current_window(win_id);
drawmode(NORMALDRAW);
reshapeviewport();
draw_it(obj);
if (double_buf)
{
swapbuffers();
draw_it(obj);
}
handle_segments(obj);
changed = 1;
clear_over_pup();
}
/*
* must reset current object and graphics pipe default regardless if we
* have drawn anything or not
*/
obj->n = obj->finished = 0;
}
/******************************************************************
* Due to hardware constraints (at most 256 vertex), we need to
* to divide objects into several pieces. This function is typicall
* called from within a pen_move
*****************************************************************/
static void
flush_object(Object_t * obj)
{
int n = obj->n;
long odraw;
Pnt_type *x = obj->x, *y = obj->y;
M_info("FlushObj", "at n=%d", n);
set_current_window(win_id);
odraw = getdrawmode();
obj->finished = 1;
paint_it(obj);
/*
* paint_it has reset obj->n == 0, need to keep last two points for
* bootstrap
*/
x[0] = x[n - 1];
y[0] = y[n - 1];
pdraw->pen_down(obj);
x[1] = x[n];
y[1] = y[n];
obj->n = 1;
pdraw->pen_move(obj);
drawmode(odraw);
}
/* ARGSUSED */
static void
null_op(Object_t * ob)
{
M_info("NullOP", "");
}
/*******************************************************************
* Leftmouse: the pen down function.
* Paint tool by default leaves marks only if we move it. Spray
* is the only exception where marks accumlates
*******************************************************************/
static void
paint_leftmouse(long dev)
{
get_current_point(&cur_obj);
set_current_window(win_id);
switch_frame_buffer();
set_line_attribute();
if (pdraw->pen_down)
pdraw->pen_down(&cur_obj);
while (getbutton(dev))
{
if (get_current_point(&cur_obj) || pdraw->spray)
pen_move(&cur_obj);
/*
* if buffer is exhausted, paint the current object and start over
*/
if (cur_obj.n >= MAX_PNTS - 1)
flush_object(&cur_obj);
}
drawmode(NORMALDRAW);
/* "pen" is up */
if (pdraw->pen_up)
pdraw->pen_up(&cur_obj);
reset_line_attribute();
}
/* ARGSUSED */
static void
paint_middlemouse(long dev)
{
if (pdraw->pen_end)
pdraw->pen_end(&cur_obj);
}
/** handle keyboard event **/
static void
paint_keybd(int val)
{
int n = cur_obj.n;
static int step = 1;
int moved = 1;
if (!pdraw->pen_motion)
return;
switch (val)
{
case 'j':
cur_obj.y[n] -= step;
break;
case 'h':
cur_obj.x[n] -= step;
break;
case 'k':
cur_obj.y[n] += step;
break;
case 'l':
cur_obj.x[n] += step;
break;
default:
moved = 0;
if (val > '0' && val <= '9')
step = val - '0';
break;
}
/* warp mouse to new location and handle this manual motion */
if (moved)
set_mouse(cur_obj.x[n] + win_xo, cur_obj.y[n] + win_yo);
}
/********************************************************************
*
* DEFINATION of all paint functions
*
******************************************************************{*/
/*************** Line *****************************{**/
static void
line_redraw(Object_t * obj)
{
draw_line(obj->x[0], obj->y[0], obj->x[1], obj->y[1]);
}
static void
line_end(Object_t * obj)
{
obj->finished = obj->n > 0;
obj->n = 2;
paint_it(obj);
}
static int ln_oldx, ln_oldy;
static void
line_down(Object_t * obj)
{
/* pen down */
if (obj->n == 0)
{
obj->n = 1;
ln_oldx = obj->x[0];
ln_oldy = obj->y[0];
return;
}
/* 2nd time around */
line_end(obj);
}
static void
line_motion(Object_t * obj)
{
Pnt_type *x = obj->x, *y = obj->y;
if (obj->n == 0)
return;
/* show size and theta, among other things */
show_rect_all(x[0], y[0], x[1] - x[0], y[1] - y[0], 1);
color(0);
draw_line(x[0], y[0], ln_oldx, ln_oldy);
color(OP_COL);
draw_line(x[0], y[0], x[1], y[1]);
ln_oldx = x[1];
ln_oldy = y[1];
}
static void
line_term(Object_t * obj)
{
line_end(obj);
hide_rect_all();
}
static Draw_t *
make_line(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = line_down;
d->pen_motion = line_motion;
d->pen_term = line_term;
d->redraw = line_redraw;
d->spray = d->closed = 0;
d->builtin = 1;
return d;
}
/************* END of lines ***************************}**/
/***************** Free hand polygons ****************{***/
static void
fpoly_redraw(Object_t * obj)
{
long xy[2];
Pnt_type *x = obj->x, *y = obj->y, *xs = x + obj->n;
for (xs = x + obj->n; x < xs; x++, y++)
{
xy[0] = *x;
xy[1] = *y;
v2i(xy);
}
}
static void
fpoly_motion(Object_t * obj)
{
int n = obj->n;
Pnt_type *x = obj->x, *y = obj->y;
static int oldx, oldy;
if (n == 0)
return;
color(0);
draw_line(x[n - 1], y[n - 1], oldx, oldy);
color(OP_COL);
draw_line(x[n - 1], y[n - 1], x[n], y[n]);
oldx = x[n];
oldy = y[n];
}
static void
fpoly_down(Object_t * obj)
{
int n = obj->n;
if (obj->n == 0)
{
obj->n = 1;
return;
}
#if 0
color(OP_COL);
draw_line(obj->x[n - 1], obj->y[n - 1], obj->x[n], obj->y[n]);
#else
fpoly_motion(obj);
#endif
obj->n++;
}
static void
fpoly_end(Object_t * obj)
{
/* remember the last point */
obj->n++;
obj->finished = 1;
paint_it(obj);
}
static void
fpoly_term(Object_t * obj)
{
obj->n = obj->finished = 0;
clear_over_pup();
}
static Draw_t *
make_fpoly(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = fpoly_down;
d->pen_move = null_op;
d->pen_motion = fpoly_motion;
d->pen_end = fpoly_end;
d->pen_term = fpoly_term;
d->redraw = fpoly_redraw;
d->outline = fpoly_redraw;
d->spray = 0;
d->closed = 1;
return d;
}
/************* END of free hand polygons ************}***/
/********************* Rays *************************{***/
static int rayshape = 2, raysize;
/*** Limit end point to some pre-defined shapes **/
static void
ray_shape_clip(Object_t * obj)
{
int n = obj->n, r;
int xx = obj->x[n] - obj->x[0];
int yy = obj->y[n] - obj->y[0];
double fact;
if (obj->n == 1 && rayshape != 1)
{
raysize = (rayshape == 2) ? sqrt(xx * xx + yy * yy) :
Max(Abs(xx), Abs(yy));
return;
}
switch (rayshape)
{
case 2: /* circle */
if (((r = xx * xx + yy * yy) > raysize * raysize))
{
fact = raysize / (0.1 + sqrt(r));
obj->x[n] = obj->x[0] + (double) xx *fact;
obj->y[n] = obj->y[0] + (double) yy *fact;
}
break;
case 3: /* square */
if (Abs(xx) > raysize)
obj->x[n] = obj->x[0] + (xx > 0 ? 1 : -1) * raysize;
if (Abs(yy) > raysize)
obj->y[n] = obj->y[0] + (yy > 0 ? 1 : -1) * raysize;
break;
}
}
static void
ray_redraw(Object_t * obj)
{
int xo = obj->x[0], yo = obj->y[0];
Pnt_type *x = obj->x, *y = obj->y, *xs = x + obj->n;
while (x < xs)
draw_line(xo, yo, *x++, *y++);
}
static int rayoldx, rayoldy;
static void
ray_down(Object_t * obj)
{
int n = obj->n;
if (n == 0)
{
obj->n = 1;
return;
}
ray_shape_clip(obj);
color(OP_COL);
draw_line(obj->x[0], obj->y[0], obj->x[n], obj->y[n]);
obj->n++;
/* invalidate motion record */
rayoldx = -1;
}
static void
ray_move(Object_t * obj)
{
/*
* we want to have the first two points seperate to define the size of
* the circle/sqr
*/
if (obj->n >= 2)
ray_down(obj);
}
static void
ray_motion(Object_t * obj)
{
int n = obj->n;
Pnt_type *x = obj->x, *y = obj->y;
if (!obj->n)
return;
ray_shape_clip(obj);
if (rayoldx > 0)
{
color(0);
draw_line(x[0], y[0], rayoldx, rayoldy);
}
/* show size and theta, among other things */
show_rect_all(x[0], y[0], x[n] - x[0], y[n] - y[0], 1);
color(OP_COL);
draw_line(x[0], y[0], x[n], y[n]);
/*
* this is necessary because near center region is wiped out by the
* moving ray. Adding this looks MUCH better
*/
ray_redraw(obj);
rayoldx = x[n];
rayoldy = y[n];
}
static void
ray_end(Object_t * obj)
{
obj->finished = 1;
paint_it(obj);
}
static void
ray_option(void)
{
static char *rayopt = "RayOption%t|Free|Circle|Square";
static long raymenu = -1;
int tray;
/* block while current object has not finished */
if (cur_obj.n > 1)
return;
if (raymenu < 0)
raymenu = defpup(rayopt);
if ((tray = dopup(raymenu)) > 0)
rayshape = tray;
}
static Draw_t *
make_ray(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->redraw = ray_redraw;
d->pen_down = ray_down;
d->pen_move = ray_move;
d->pen_end = ray_end;
d->pen_motion = ray_motion;
d->option = ray_option;
d->closed = 0;
d->builtin = 1;
return d;
}
/************** END of Rays ***********************}***/
/********************* Erasor *********************{**/
static int ex = 20, ey = 20;
static void
erasor_term(Object_t * obj)
{
rubber_finish();
obj->n = obj->finished = 0;
}
static void
erasor_move(Object_t * obj)
{
int n = obj->n;
int xi = obj->x[n] - ex / 2, yi = obj->y[n] - ey / 2;
drawmode(NORMALDRAW);
reshapeviewport();
rubber_moveto(&xi, &yi, &ex, &ey);
/* redraw the area covered by rubber band */
dbl_rect_redraw(saved, xi, yi, ex, ey);
/* change the image in core */
fb_to_ras(imgptr, make_rect(xi, yi, ex, ey));
}
static void
erasor_motion(Object_t * obj)
{
int n = obj->n;
int xi = obj->x[n] - ex / 2, yi = obj->y[n] - ey / 2;
short val;
linewidth(1);
set_rubber_bounds(1, imgptr->xi, imgptr->yi, imgptr->w, imgptr->h);
if (n == 0)
{
set_rubber_obj(RB_RECT);
(void) rubber_cursor(win_id, &val, &xi, &yi, ex, ey, 2);
obj->n = 1;
}
rubber_moveto(&xi, &yi, &ex, &ey);
}
static void
erasor_option(void)
{
static long emenu = -1;
int n;
if (emenu < 0)
emenu = defpup("Erasor%t|10x10|20x20|30x30|40x40|50x50");
if ((n = dopup(emenu)) > 0)
ex = ey = 10 * n;
}
static Draw_t *
make_erasor(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = erasor_move;
d->redraw = null_op;
d->pen_move = erasor_move;
d->pen_motion = erasor_motion;
d->pen_term = erasor_term;
d->option = erasor_option;
d->closed = 0;
d->builtin = 1;
return d;
}
/************ END of erasor **********************************}***/
/******************* Air Brush **************{**/
#define MAX_AB_SIZE 35 /* max brush size */
static int abx = 20; /* air brush size */
static int aby = 20; /* air brush size */
static FL_FORM *create_form_fspray(void);
static void spray_option(void);
#ifdef __STRICT_ANSI__
extern long random(void);
#endif
/* flow rate is something artificial, 1.0 being max */
static float arate = 0.5; /* flow rate */
/***** Generate "good" points **/
static void
get_circ_points(int n, Pnt_type * x, Pnt_type * y, int range)
{
int rsq = range * range;
int i;
for (i = 0; i < n;)
{
*x = range * (float) ((random() % 10001) / 10000.0 * 2.0 - 1.0);
*y = range * (float) ((random() % 10001) / 10000.0 * 2.0 - 1.0);
if ((*x) * (*x) + (*y) * (*y) < rsq)
{
i++;
x++;
y++;
}
}
}
static void
get_rect_points(int n, Pnt_type * x, Pnt_type * y, int range)
{
int i;
for (i = 0; i < n;)
{
*x = range * (float) ((random() % 10001) / 10000.0 * 2.0 - 1.0);
*y = range * (float) ((random() % 10001) / 10000.0 * 2.0 - 1.0);
if (*x < range && *y < range)
{
i++;
x++;
y++;
}
}
}
static int
points_to_spray(int N)
{
int m;
return ((m = 0.25 * arate * abx * aby) > N / 2) ? N / 2 : ((m < 8) ? 8 : m);
}
/*
* instead of letting paint driver do the rasterization, we can
* manipulate the virtual framebuffer directly
*/
static void
spray_and_rasterize(Pnt_type * xx, Pnt_type * yy, int m, int x, int y)
{
register rgba_t cc = Pack(ccol[0], ccol[1], ccol[2]);
register int xi = imgptr->xi, yi = imgptr->yi;
register int w = imgptr->w, h = imgptr->h;
register int i, px, py;
register rgba_t **vfb = imgptr->mraster;
long xy[2];
bgnpoint();
for (i = 0; i < m; i++)
{
xy[0] = x + xx[i];
xy[1] = y + yy[i];
v2i(xy);
/* px, py are relative to image */
px = xy[0] - xi;
py = xy[1] - yi;
if (px >= 0 && py >= 0 && px < w && py < h)
vfb[py][px] = cc;
}
endpoint();
}
static void
circ_airbrush(Object_t * obj)
{
int n = obj->n, m;
Pnt_type xx[512], yy[512];
int x = obj->x[n], y = obj->y[n];
m = points_to_spray(512);
get_circ_points(m, xx, yy, abx);
spray_and_rasterize(xx, yy, m, x, y);
}
static void
rect_airbrush(Object_t * obj)
{
int n = obj->n, m;
int x = obj->x[n], y = obj->y[n];
Pnt_type xx[512], yy[512];
m = points_to_spray(512);
get_rect_points(m, xx, yy, abx);
spray_and_rasterize(xx, yy, m, x, y);
}
/** Looks nicer to use more HVHASH **/
static int beffects[] =
{
PAT_RHASH, PAT_GRID_DOTS, PAT_LHASH, PAT_HVHASH, PAT_HHASH,
PAT_GRID_DOTS, PAT_HVHASH,
};
static int cur_effects, neffects = sizeof(beffects) / sizeof(beffects[0]);
static void
net_airbrush(Object_t * obj)
{
int n = obj->n;
int x_n = obj->x[n], y_n = obj->y[n];
static int lx, ly;
/* add a threshold make it better on fast machines */
if (Abs(x_n - lx) > abx / 3 || Abs(y_n - ly) > aby / 3)
{
setpattern(beffects[cur_effects++]);
gl_circ(x_n, y_n, 2 * abx - 1, 2 * aby - 1, 1, 0);
cur_effects %= neffects;
lx = x_n;
ly = y_n;
setpattern(0);
}
}
typedef struct
{
char *bitmap; /* nozzle shape bitmap */
int cur_min; /* min. size cursor */
int cur_max; /* max. size cursor */
void (*spray) (Object_t *); /* how */
}
AirBrush;
static AirBrush abrushes[] =
{
{circ_bits, CUR_CIRC10, CUR_CIRC30, circ_airbrush},
{rec_bits, CUR_RECT10, CUR_RECT30, rect_airbrush},
{ray_bits, CUR_CIRCNET, CUR_CIRCNET, net_airbrush}
};
static int cur_abrush;
static void (*spray_it) (Object_t *);
/* ARGSUSED */
static void
spray_init(Object_t * obj)
{
AirBrush *ab = abrushes + cur_abrush;
int cursor;
reset_time();
cursor = ab->cur_min + (abx / 10);
if (cursor > ab->cur_max)
cursor = ab->cur_max;
set_cursor(win_id, cursor);
set_default_cursor(win_id, cursor);
spray_it = ab->spray;
}
/**************************************************************/
static void
spray_move(Object_t * obj)
{
static int lx, ly;
int n = obj->n;
#if 1
int t = (1.0 / (arate + 0.01));
t = (t > 20) ? 20 : t;
if ((lx == obj->x[n]) && (ly == obj->y[n]) && time_passed() < t)
return;
reset_time();
#endif
set_current_window(win_id);
drawmode(NORMALDRAW);
reshapeviewport();
frontbuffer(1);
backbuffer(1);
Color4(ccol);
spray_it(obj);
if ((lx - obj->x[n]) || (ly - obj->y[n]))
{
lx = obj->x[n];
ly = obj->y[n];
obj->n++;
}
frontbuffer(!double_buf);
}
static void
spray_up(Object_t * obj)
{
int oldlw = cur_lw;
/* only net_spray need to be rasterized by the paint driver */
if (spray_it == net_airbrush)
{
obj->n++;
obj->finished = 1;
/* accumalation routine take lw/2 */
/* cur_lw = MAX_AB_SIZE; */
cur_lw = Max(abx, aby);
paint_it(obj);
cur_lw = oldlw;
}
obj->n = obj->finished = 0;
}
/* ARGSUSED */
static void
spray_term(Object_t * obj)
{
set_default_cursor(win_id, CUR_DEFAULT);
reset_cursor(win_id);
}
static Draw_t *
make_spray(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_init = spray_init;
d->pen_down = spray_move;
d->pen_move = spray_move;
d->pen_up = spray_up;
d->redraw = null_op;
d->pen_term = spray_term;
d->option = spray_option;
d->spray = 1;
d->builtin = 1;
return d;
}
/*** get spray options ****/
static void
spray_option(void)
{
short val;
bit_show_form(create_form_fspray(), FL_PLACE_MOUSE, 0, "SprayOption");
while (bit_qread(&val) != F1KEY || !val)
;
bit_hide_form(create_form_fspray());
}
/* ARGSUSED */
static void
spray_tools(FL_OBJECT * ob, long q)
{
flush_object(&cur_obj);
cur_abrush = q;
spray_init(&cur_obj);
}
/*ARGSUSED */
static void
spray_size_cb(FL_OBJECT * ob, long q)
{
flush_object(&cur_obj);
abx = aby = fl_get_counter_value(ob);
spray_init(&cur_obj);
}
/*ARGSUSED */
static void
spray_rate_cb(FL_OBJECT * ob, long q)
{
arate = fl_get_slider_value(ob);
}
/*ARGSUSED */
static void
spray_opt_end(FL_OBJECT * ob, long q)
{
fl_qenter(F1KEY, 1);
}
static FL_FORM *
create_form_fspray(void)
{
static FL_FORM *fspray;
FL_OBJECT *obj;
float x, y, dx, dy;
int i;
if (fspray)
return fspray;
fspray = fl_bgn_form(FL_NO_BOX, 190.0, 215.0);
obj = fl_add_box(FL_UP_BOX, 0.0, 0.0, 190.0, 215.0, "");
fl_set_object_color(obj, 12, 47);
obj = fl_add_box(FL_DOWN_BOX, 10.0, 45.0, 170.0, 160.0, "");
fl_set_object_color(obj, 9, 47);
fl_bgn_group();
x = 30;
y = 155;
dx = 40;
dy = 40;
for (i = 0; i < 3; i++, x += dx + 5)
{
obj = fl_add_active_bitmap(FL_RADIO_BITMAP, x, y, dx, dy, "");
fl_set_bitmap_bitmap(obj, BITMAP_W, BITMAP_H, abrushes[i].bitmap);
fl_set_call_back(obj, spray_tools, i);
if (i == cur_abrush)
fl_set_active_bitmap(obj, 1);
}
fl_end_group();
obj = fl_add_valslider(FL_HNS, 20.0, 55.0, 150.0, 25.0, "FlowRate");
fl_set_object_color(obj, 9, 47);
fl_set_object_lsize(obj, 10.000000);
fl_set_object_align(obj, FL_ALIGN_TOP);
fl_set_slider_bounds(obj, 0.01, 1.0);
fl_set_slider_value(obj, 0.5);
fl_set_call_back(obj, spray_rate_cb, 0);
obj = fl_add_counter(FL_NC, 30.0, 105.0, 130.0, 25.0, "NozzleSize");
fl_set_object_lsize(obj, 10.000000);
fl_set_object_align(obj, FL_ALIGN_TOP);
fl_set_counter_step(obj, 1, 5);
fl_set_counter_bounds(obj, 5, 32);
fl_set_counter_value(obj, 10);
fl_set_call_back(obj, spray_size_cb, 0);
obj = fl_add_button(FL_NORMAL_BUTTON, 125.0, 10.0, 55.0, 25.0, "OK");
fl_set_object_lsize(obj, 10.000000);
fl_set_call_back(obj, spray_opt_end, 0);
fl_end_form();
return fspray;
}
/***** END of Spray function ******************* }**/
static int
if_center(void)
{
static long centered = -1;
if (centered < 0)
centered = defpup("CenterOpt%t|default|center");
return dopup(centered) - 1;
}
/************** Circles **************************************{**/
static int circ_centered;
static int circ_x, circ_y, circ_dx, circ_dy;
static void
circ_position(int xi, int yi, int xf, int yf)
{
if (circ_centered)
{
circ_x = xi;
circ_y = yi;
circ_dx = 2 * (xf - xi);
circ_dy = 2 * (yf - yi);
}
else
{
circ_x = (xi + xf) / 2;
circ_y = (yi + yf) / 2;
circ_dx = xf - xi + 1;
circ_dy = yf - yi + 1;
}
/* again, need to canonicalize the size */
if (circ_dx < 0)
circ_dx = -circ_dx;
if (circ_dy < 0)
circ_dy = -circ_dy;
}
static void
circ_redraw(Object_t * obj)
{
circ_position(obj->x[0], obj->y[0], obj->x[1], obj->y[1]);
gl_circ(circ_x, circ_y, circ_dx, circ_dy, cur_pat != PAT_HOLLOW, 0);
}
static void
circ_outline(Object_t * obj)
{
int opat = cur_pat;
cur_pat = PAT_HOLLOW;
circ_redraw(obj);
cur_pat = opat;
}
static void
circ_move(Object_t * obj)
{
static int oldx, oldy;
Pnt_type *x = obj->x, *y = obj->y;
if (obj->n == 0)
{
oldx = x[0];
oldy = y[0];
obj->n = 1;
return;
}
circ_position(x[0], y[0], oldx, oldy);
color(0);
gl_circ(circ_x, circ_y, circ_dx, circ_dy, 0, 0);
circ_position(x[0], y[0], x[1], y[1]);
color(OP_COL);
gl_circ(circ_x, circ_y, circ_dx, circ_dy, 0, 0);
oldx = x[1];
oldy = y[1];
}
static void
circ_up(Object_t * obj)
{
obj->n = 2;
obj->finished = 1;
/*
* due to two different type of circles, need to handle segment manually
* here. Also due to anti-aliasing, give a couple of more pixels to save
*/
circ_position(obj->x[0], obj->y[0], obj->x[1], obj->y[1]);
add_segment(circ_x - circ_dx / 2 - 1, circ_y - circ_dy / 2 - 1,
circ_dx + 2, circ_dy + 2);
paint_it(obj);
}
static void
circ_option(void)
{
circ_centered = if_center() == 1;
}
static Draw_t *
make_circ(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = circ_move;
d->pen_move = circ_move;
d->redraw = circ_redraw;
d->pen_up = circ_up;
d->option = circ_option;
d->outline = circ_outline;
d->closed = 1;
d->builtin = 1;
return d;
}
/***** END of circles **********************}*****/
/************** Rectangles ********************************{****/
static int sqr_centered;
static int sqr_x, sqr_y, sqr_dx, sqr_dy;
static void
sqr_position(int xi, int yi, int xf, int yf)
{
if (sqr_centered)
{
sqr_x = xi;
sqr_y = yi;
sqr_dx = 2 * (xf - xi);
sqr_dy = 2 * (yf - yi);
}
else
{
sqr_x = (xi + xf) / 2;
sqr_y = (yi + yf) / 2;
sqr_dx = (xf - xi);
sqr_dy = (yf - yi);
}
if (sqr_dx < 0)
sqr_dx = -sqr_dx;
if (sqr_dy < 0)
sqr_dy = -sqr_dy;
}
static void
sqr_redraw(Object_t * obj)
{
sqr_position(obj->x[0], obj->y[0], obj->x[1], obj->y[1]);
gl_rect(sqr_x, sqr_y, sqr_dx, sqr_dy, cur_pat != PAT_HOLLOW, 0);
}
static void
sqr_outline(Object_t * obj)
{
int opat = cur_pat;
cur_pat = PAT_HOLLOW;
sqr_redraw(obj);
cur_pat = opat;
}
static void
sqr_move(Object_t * obj)
{
static int oldx, oldy;
if (obj->n == 0)
{
oldx = obj->x[0];
oldy = obj->y[0];
obj->n = 1;
return;
}
color(0);
sqr_position(obj->x[0], obj->y[0], oldx, oldy);
gl_rect(sqr_x, sqr_y, sqr_dx, sqr_dy, 0, 0);
color(OP_COL);
sqr_position(obj->x[0], obj->y[0], obj->x[1], obj->y[1]);
gl_rect(sqr_x, sqr_y, sqr_dx, sqr_dy, 0, 0);
oldx = obj->x[1];
oldy = obj->y[1];
}
static void
sqr_up(Object_t * obj)
{
obj->n = 2;
obj->finished = 1;
/*
* due to two different type of rectangles, need to handle segment
* manually here
*/
sqr_position(obj->x[0], obj->y[0], obj->x[1], obj->y[1]);
add_segment(sqr_x - sqr_dx / 2 - 1, sqr_y - sqr_dy / 2 - 1,
sqr_dx + 2, sqr_dy + 2);
paint_it(obj);
}
static void
sqr_option(void)
{
sqr_centered = if_center() == 1;
}
static Draw_t *
make_sqr(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = sqr_move;
d->pen_move = sqr_move;
d->redraw = sqr_redraw;
d->pen_up = sqr_up;
d->option = sqr_option;
d->outline = sqr_outline;
d->closed = 1;
d->builtin = 1;
return d;
}
/************* END of rectanges ****************}***/
/**** Pencils ***********************{***/
static void
pencil_redraw(Object_t * obj)
{
float xy[2];
Pnt_type *x = obj->x, *y = obj->y, *xs = x + obj->n;
if (cur_lw <= 0)
return;
while (x < xs)
{
xy[0] = *x++;
xy[1] = *y++;
v2f(xy);
}
}
static void
pencil_move(Object_t * obj)
{
int n = obj->n;
if (obj->n == 0)
{
obj->n = 1;
return;
}
color(OP_COL);
draw_line(obj->x[n - 1], obj->y[n - 1], obj->x[n], obj->y[n]);
/* can't have more than 256 vertex */
if (obj->n > 250)
flush_object(obj);
else
obj->n++;
}
static void
pencil_up(Object_t * obj)
{
obj->finished = 1;
paint_it(obj);
}
static Draw_t *
make_pencil(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = pencil_move;
d->pen_up = pencil_up;
d->pen_move = pencil_move;
d->redraw = pencil_redraw;
d->closed = 0;
return d;
}
/************ END of pencils ************************}****/
/************* CUT & PASTE ***************************{*/
static void **cutbuf; /* raster being cut */
static Rect_t cutsize; /* cut size */
static int cp_oldx, cp_oldy; /* position of pen_down */
/*************************************************************
* check if current mouse click is within the rectangle formed
* by point 0 and point 1
*************************************************************/
static int
inbounds(Object_t * obj)
{
const Rect_t *r;
Pnt_type *x = obj->x, *y = obj->y;
if (obj->n < 2)
return 0;
r = make_rect(x[0], y[0], x[1] - x[0] + 1, y[1] - y[0] + 1);
return inside_rect(x[2], y[2], r);
}
static void
cut_it(Object_t * obj)
{
Rect_t rdummy;
Rect_t *r = &rdummy;
Pnt_type *x = obj->x, *y = obj->y;
if (cutbuf)
return;
copy_rect(r, make_rect(x[0], y[0], x[1] - x[0] + 1, y[1] - y[0] + 1));
canonicalize_rect(r);
/* canonicalize the obeject as well */
x[0] = r->x;
y[0] = r->y;
x[1] = r->x + r->w - 1;
y[1] = r->y + r->h - 1;
cutbuf = no_fail_get_subimage(imgptr, r->x, r->y, r->w, r->h);
cutsize = *r;
}
static void
cp_down(Object_t * obj)
{
if (obj->n < 2)
return;
clear_over_pup();
if (!inbounds(obj))
{
set_current_window(win_id);
switch_frame_buffer();
obj->n = 0;
obj->x[0] = obj->x[2];
obj->y[0] = obj->y[2];
}
else
{
cut_it(obj);
}
cp_oldx = obj->x[2];
cp_oldy = obj->y[2];
}
static void
cp_move(Object_t * obj)
{
int dx, dy;
Rect_t *cs = &cutsize;
if (obj->n < 2)
{
sqr_move(obj);
if (cutbuf)
{
free_mat(cutbuf);
cutbuf = 0;
drawmode(NORMALDRAW);
dbl_rect_redraw(imgptr, cs->x, cs->y, cs->w, cs->h);
switch_frame_buffer();
}
return;
}
set_current_window(win_id);
drawmode(NORMALDRAW);
reshapeviewport();
dx = obj->x[2] - cp_oldx;
dy = obj->y[2] - cp_oldy;
shift_object(obj, dx, dy);
mv_ras_obj(cutbuf, cs->w, cs->h, cs->x, cs->y, cs->x + dx, cs->y + dy);
shift_rect(cs, dx, dy);
cp_oldx = obj->x[2];
cp_oldy = obj->y[2];
switch_frame_buffer();
}
static void
cp_up(Object_t * obj)
{
set_current_window(win_id);
switch_frame_buffer();
obj->n = 1;
color(OP_COL);
sqr_move(obj);
obj->n = 2;
drawmode(NORMALDRAW);
}
static void
cp_redraw(Object_t * obj)
{
Rect_t *cs = &cutsize;
/*
* after a window manager redraw event, the rectangle and cut buffer
* might be at different locations
*/
if (obj->n >= 2 && (cs->x != obj->x[0] || cs->y != obj->y[0]))
{
cs->x = obj->x[0];
cs->y = obj->y[0];
}
}
/* ARGSUSED */
static void
cp_end(Object_t * obj)
{
Rect_t *cs = &cutsize;
/*
* here we operate on the image directly to avoid reading a (possibly)
* dithered framebuffer
*/
if (cutbuf)
{
put_subimage(imgptr, cutbuf, cs, 0);
dbl_rect_redraw(imgptr, cs->x, cs->y,
cs->x + cs->w - 1, cs->y + cs->h - 1);
}
}
static void
cp_term(Object_t * obj)
{
obj->n = obj->finished = 0;
cp_move(obj);
obj->n = 0;
clear_over_pup();
drawmode(NORMALDRAW);
reshapeviewport();
}
static void
cp_motion(Object_t * obj)
{
if (obj->n == 0)
return;
set_cursor(win_id, inbounds(obj) ? CUR_HAND : CUR_DEFAULT);
}
static Draw_t *
make_cut_paste(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = cp_down;
d->pen_move = cp_move;
d->pen_up = cp_up;
d->pen_end = cp_end;
d->pen_term = cp_term;
d->pen_motion = cp_motion;
d->redraw = cp_redraw;
return d;
}
/**** END OF cut&paste ****************/
/*****************************************************************
* Brush
* Based on "stroke" model: any pen moved is acomplished by
* stroking from (xi,yi) to (xf, yf)
**************************************************************{**/
#define BS_BW 20 /* brush bitmap width */
#define BS_BH 20 /* brush bitmap height */
#include "bitmaps/bs_circ.xbm"
#include "bitmaps/bs_rline.xbm"
#include "bitmaps/bs_lline.xbm"
#include "bitmaps/bs_rect.xbm"
#include "bitmaps/bs_hline.xbm"
#include "bitmaps/bs_vline.xbm"
typedef struct
{
int cursor_name; /* cursor names */
char *bits; /* brush shape bitmaps */
void (*stroke) (int, int, int, int);
}
Brush_t;
static int get_brush_shape(void);
static void rline_stroke(int, int, int, int);
static void lline_stroke(int, int, int, int);
static void hline_stroke(int, int, int, int);
static void vline_stroke(int, int, int, int);
static void circ_stroke(int, int, int, int);
static void rect_stroke(int, int, int, int);
static Brush_t brushes[] =
{
{CUR_L_LINE, bslline_bits, lline_stroke},
{CUR_R_LINE, bsrline_bits, rline_stroke},
{CUR_H_LINE, bshline_bits, hline_stroke},
{CUR_V_LINE, bsvline_bits, vline_stroke},
{CUR_S_DISK, bscirc_bits, circ_stroke},
{CUR_S_FRECT, bsrect_bits, rect_stroke},
};
static int nbrushes = sizeof(brushes) / sizeof(brushes[0]);
static Brush_t *cur_brush = &brushes[0];
static void (*cur_stroke) (int, int, int, int);
static int bx = 10, by = 10; /* brush size. better to even */
static void
hline_stroke(int xi, int yi, int xf, int yf)
{
float xy[2];
bgnpolygon();
{
xy[0] = xi - bx / 2;
xy[1] = yi;
v2f(xy);
xy[0] = xi + bx / 2;
xy[1] = yi;
v2f(xy);
xy[0] = xf + bx / 2;
xy[1] = yf;
v2f(xy);
xy[0] = xf - bx / 2;
xy[1] = yf;
v2f(xy);
}
endpolygon();
}
static void
vline_stroke(int xi, int yi, int xf, int yf)
{
float xy[2];
bgnpolygon();
{
xy[0] = xi;
xy[1] = yi + by / 2;
v2f(xy);
xy[0] = xi;
xy[1] = yi - by / 2;
v2f(xy);
xy[0] = xf;
xy[1] = yf - by / 2;
v2f(xy);
xy[0] = xf;
xy[1] = yf + by / 2;
v2f(xy);
}
endpolygon();
}
/* brush shape is a left-slanted line / */
static void
lline_stroke(int xi, int yi, int xf, int yf)
{
float xy[2];
bgnpolygon();
{
xy[0] = xi - bx / 2;
xy[1] = yi - by / 2;
v2f(xy);
xy[0] = xi + bx / 2;
xy[1] = yi + by / 2;
v2f(xy);
xy[0] = xf + bx / 2;
xy[1] = yf + by / 2;
v2f(xy);
xy[0] = xf - bx / 2;
xy[1] = yf - by / 2;
v2f(xy);
}
endpolygon();
}
/* brush shape is a right-slanted line \ */
static void
rline_stroke(int xi, int yi, int xf, int yf)
{
float xy[2];
bgnpolygon();
{
xy[0] = xi - bx / 2;
xy[1] = yi + by / 2;
v2f(xy);
xy[0] = xi + bx / 2;
xy[1] = yi - by / 2;
v2f(xy);
xy[0] = xf + bx / 2;
xy[1] = yf - by / 2;
v2f(xy);
xy[0] = xf - bx / 2;
xy[1] = yf + by / 2;
v2f(xy);
}
endpolygon();
}
/* ARGSUSED */
static void
circ_stroke(int xi, int yi, int xf, int yf)
{
gl_circ(xi, yi, bx, by, 1, 0);
}
/* ARGSUSED */
static void
rect_stroke(int xi, int yi, int xf, int yf)
{
gl_rect(xi, yi, bx, by, 1, 0);
}
static void
brush_redraw(Object_t * obj)
{
Pnt_type *x = obj->x, *y = obj->y, *xs = x + obj->n;
for (--xs; x < xs; x++, y++)
cur_stroke(*x, *y, *(x + 1), *(y + 1));
}
static void
brush_move(Object_t * obj)
{
int n = obj->n;
if (obj->n == 0)
{
obj->n++;
return;
}
color(OP_COL);
setpattern(cur_pat);
cur_stroke(obj->x[n - 1], obj->y[n - 1], obj->x[n], obj->y[n]);
obj->n++;
setpattern(0);
}
static void
brush_init(Object_t * obj)
{
obj->n = obj->finished = 0;
cur_stroke = cur_brush->stroke;
set_cursor(win_id, cur_brush->cursor_name);
set_default_cursor(win_id, cur_brush->cursor_name);
}
/* ARGSUSED */
static void
brush_term(Object_t * obj)
{
set_default_cursor(win_id, CUR_DEFAULT);
reset_cursor(win_id);
}
static void
brush_up(Object_t * obj)
{
int savelw = cur_lw;
cur_lw = Max(bx, by);
obj->finished = 1;
paint_it(obj);
cur_lw = savelw;
}
static void
brush_option(void)
{
int cur_bs = get_brush_shape();
if (cur_bs >= 0)
{
/* flush last object */
if (pdraw->pen_end)
pdraw->pen_end(&cur_obj);
paint_it(&cur_obj);
cur_brush = brushes + cur_bs;
pdraw = make_brush();
pen_move = pdraw->pen_move ? pdraw->pen_move : null_op;
brush_init(&cur_obj);
}
}
static Draw_t *
make_brush(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_init = brush_init;
d->pen_down = brush_move;
d->pen_move = brush_move;
d->pen_up = brush_up;
d->pen_term = brush_term;
d->redraw = brush_redraw;
d->option = brush_option;
d->builtin = 1;
d->closed = 1;
return d;
}
/*********** GUI for setting brush shapes */
static FL_FORM *create_bs_form(void);
static int guibs;
static int
get_brush_shape(void)
{
short val;
fl_deactivate_all_forms();
bit_show_form(create_bs_form(), FL_PLACE_HOTSPOT, 0, "");
while (bit_qread(&val) != KEYBD || val != 27)
;
bit_hide_form(create_bs_form());
fl_activate_all_forms();
return guibs;
}
/* ARGSUSED */
static void
bs_cb(FL_OBJECT * ob, long q)
{
guibs = q;
fl_qenter(KEYBD, 27);
}
static FL_FORM *
create_bs_form(void)
{
FL_OBJECT *obj;
static FL_FORM *bshape;
int w = BS_BW + 18, h = 40 + nbrushes * (BS_BH + 3);
float x, y, dx, dy;
int i;
if (bshape)
return bshape;
bshape = fl_bgn_form(FL_FLAT_BOX, w, h);
fl_bgn_group();
dx = BS_BW + 6;
dy = BS_BH + 6;
x = 5;
y = h - 35;
for (i = 0; i < nbrushes; i++, y -= dy)
{
obj = fl_add_active_bitmap(FL_RADIO_BITMAP, x, y, dx, dy, "");
fl_set_bitmap_bitmap(obj, BS_BW, BS_BH, brushes[i].bits);
fl_set_call_back(obj, bs_cb, i);
fl_set_active_bitmap(obj, i == 0);
}
fl_end_group();
fl_end_form();
fl_set_form_hotspot(bshape, 1, h - 1);
return bshape;
}
/***********************************************************************
* END of brushes
********************************************************************}**/
/**************** arcs ***************************{***/
/**********************************************************************
* Mark control points on screen
**********************************************************************/
static short marks[2][20];
static int nmarks;
static void
mark_it(int x, int y)
{
switch_frame_buffer();
color(OP_COL);
gl_plus(x, y, 8, 8, 0, 0);
drawmode(NORMALDRAW);
marks[0][nmarks] = x;
marks[1][nmarks] = x;
nmarks++;
}
static void
remove_marks(void)
{
clear_over_pup();
nmarks = 0;
}
#define ARC_FAN 0
#define ARC_ARC 1
static int carc = ARC_FAN;
static int arcr;
static void
draw_arc(Object_t * obj, int cx, int cy)
{
Pnt_type *x = obj->x, *y = obj->y;
Coord a1, a2;
int dx, dy;
/* arc only now */
if (obj->n < 2)
return;
/* get angles */
dx = obj->x[1] - obj->x[0];
dy = obj->y[1] - obj->y[0];
a1 = dy ? atan2(dy, dx) : 0;
dx = cx - x[0];
dy = cy - y[0];
a2 = dy ? atan2(dy, dx) : 0;
/* arc routines acccepts decidegrees */
a1 *= 10 * 180 / M_PI;
a2 *= 10 * 180 / M_PI;
(carc == ARC_FAN ? arcf : arc) (x[0], y[0], arcr, a1, a2);
}
static void
draw_arc_outline(Object_t * obj, int cx, int cy)
{
Pnt_type *x = obj->x, *y = obj->y;
Coord a1, a2;
int dx, dy;
if (obj->n < 2)
return;
/* get angles */
dx = x[1] - x[0];
dy = y[1] - y[0];
a1 = dy ? atan2(dy, dx) : 0;
dx = cx - x[0];
dy = cy - y[0];
a2 = dy ? atan2(dy, dx) : 0;
if (carc == ARC_FAN)
{
draw_line(x[0], y[0], x[1], y[1]);
/* Note that can't simple draw from 0->2, need to clip to arcr */
draw_line(x[0], y[0], x[0] + arcr * cos(a2), y[0] + arcr * sin(a2));
}
/* arc routines acccepts decidegrees */
a1 *= 10 * 180 / M_PI;
a2 *= 10 * 180 / M_PI;
arc(x[0], y[0], arcr, a1, a2);
}
static void
arc_outline(Object_t * obj)
{
draw_arc_outline(obj, obj->x[2], obj->y[2]);
}
static void
arc_init(Object_t * obj)
{
obj->n = obj->finished = 0;
pdraw->closed = carc == ARC_FAN;
pdraw->outline = pdraw->closed ? arc_outline : 0;
}
static void
arc_term(Object_t * obj)
{
obj->n = 0;
remove_marks();
}
static void
arc_option(void)
{
static char *copt = "ArcOpt%t|Fan|Arc";
static long cvmenu = -1;
/* block while current object has not finished */
if (cur_obj.n > 0)
return;
if (cvmenu < 0)
cvmenu = defpup(copt);
switch (dopup(cvmenu))
{
case 1:
carc = ARC_FAN;
break;
case 2:
carc = ARC_ARC;
break;
}
arc_init(&cur_obj);
}
static void
arc_redraw(Object_t * obj)
{
draw_arc(obj, obj->x[obj->n], obj->y[obj->n]);
}
static void
arc_motion(Object_t * obj)
{
static int ox = -1, oy;
switch_frame_buffer();
if (ox > 0)
{
color(0);
draw_arc_outline(obj, ox, oy);
}
color(OP_COL);
draw_arc_outline(obj, obj->x[2], obj->y[2]);
ox = obj->x[2];
oy = obj->y[2];
drawmode(NORMALDRAW);
}
static void
arc_end(Object_t * obj)
{
if (obj->n >= 2)
{
obj->n = 2;
obj->finished = 1;
add_segment(obj->x[0] - arcr - 1, obj->y[0] - arcr - 1,
2 * arcr + 2, 2 * arcr + 2);
paint_it(obj);
remove_marks();
}
}
static void
arc_down(Object_t * obj)
{
if (obj->n == 0)
{
/* mark center */
mark_it(obj->x[0], obj->y[0]);
}
obj->n++;
if (obj->n == 2)
{
int dx, dy;
/* calculate radius */
dx = obj->x[1] - obj->x[0];
dy = obj->y[1] - obj->y[0];
arcr = sqrt(dx * dx + dy * dy);
}
if (obj->n == 3)
arc_end(obj);
}
static Draw_t *
make_arc(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_init = arc_init;
d->pen_down = arc_down;
d->pen_motion = arc_motion;
d->pen_end = arc_end;
d->pen_term = arc_term;
d->option = arc_option;
d->redraw = arc_redraw;
d->outline = arc_outline;
d->builtin = 1;
return d;
}
/******* END of arcs ********************} */
/*************** Clip-Art imports **********************{**/
static void
import_init(Object_t * obj)
{
}
static Draw_t *
make_import(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_init = import_init;
return d;
}
/************** 3D objects. ***************{**/
#define NO3D
static int obj3dok;
#ifndef NO3D
#include "obj3d.h"
#include "light.h"
static void
obj3d_init(Object_t * obj)
{
if (!(obj3dok = getgdesc(GD_BITS_NORM_ZBUFFER) > 0))
return;
init_material();
init_light();
set_material();
set_light_souce(1);
}
#endif
static void
obj3d_down(Object_t * obj)
{
if (!obj3dok)
{
Bark("3D Obj", "No zbuffer or not written");
return;
}
}
static Draw_t *
make_obj3d(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_down = obj3d_down;
return d;
}
/********** END of 3D objects. *********}**/
/************** FILL : seed fill *****************/
/* ARGSUSED */
static void
fill_init(Object_t * obj)
{
set_default_cursor(win_id, CUR_S_CROSS);
set_cursor(win_id, CUR_S_CROSS);
}
/* ARGSUSED */
static void
fill_term(Object_t * obj)
{
set_default_cursor(win_id, CUR_DEFAULT);
set_cursor(win_id, CUR_DEFAULT);
}
/*
* Based on
* A Seed Fill Algorithm
* by Paul Heckbert
* from "Graphics Gems", Academic Press, 1990
*/
typedef struct
{
short y, xl, xr, dy;
}
FSeg;
#define MAX 20000
#define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \
if (sp<stack+MAX && Y+(DY)>=0 && Y+(DY)<win->h) \
{sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
#define POP(Y, XL, XR, DY) /* pop segment off stack */ \
{sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
#define pixelread(x, y) mat[y][x]
/*****************************************************************
* we not only draw the changed pixels, we also change the image
* in core on the fly
*****************************************************************/
#define pixelwrite(x,y, nv) \
do \
{ \
mat[y][x] = nv; \
pp[0] = x+imgptr->xi; pp[1]=y+imgptr->yi; \
cpack(mat[y][x]); v2i(pp); \
} \
while(ZERO)
#define Pixel rgba_t
/* (x,y) is the seed point. Return -1 for failure, 0 otherwise */
static void
seed_fill(int x, int y, Rect_t * win, rgba_t nv)
{
int l, x1, x2, dy;
Pixel ov; /* old pixel value */
FSeg stack[MAX], *sp = stack; /* stack of filled segments */
rgba_t **mat = imgptr->mraster;
long pp[2];
if ((ov = pixelread(x, y)) == nv)
return;
changed = 1;
show_busy("Just a moment ...");
set_current_window(win_id);
bgnpoint();
PUSH(y, x, x, 1); /* needed in some cases */
PUSH(y + 1, x, x, -1); /* seed segment (popped 1st) */
while (sp > stack)
{
/* pop segment off stack and fill a neighboring scan line */
POP(y, x1, x2, dy);
/*
* segment of scan line y-dy for x1<=x<=x2 was previously filled,
* now explore adjacent pixels in scan line y
*/
for (x = x1; x >= 0 && pixelread(x, y) == ov; x--)
pixelwrite(x, y, nv);
if (x >= x1)
goto skip;
l = x + 1;
if (l < x1)
PUSH(y, l, x1 - 1, -dy); /* leak on left */
x = x1 + 1;
do
{
for (; x <= (win->w - 1) && pixelread(x, y) == ov; x++)
pixelwrite(x, y, nv);
PUSH(y, l, x - 1, dy);
if (x > x2 + 1)
PUSH(y, x2 + 1, x - 1, -dy); /* leak on right? */
skip:
for (x++; x <= x2 && pixelread(x, y) != ov; x++)
;
l = x;
}
while (x <= x2);
}
endpoint();
end_busy();
}
static void
fill_up(Object_t * obj)
{
const Rect_t *ir;
Rect_t dummyr;
if (!inside_rect(obj->x[0], obj->y[0], ir = img_rect(imgptr)))
return;
/*
* due to the way ir is used in seed_fill, need to translate the
* coordinates relative to lower left corner of the image
*/
copy_rect(&dummyr, ir);
shift_rect(&dummyr, -imgptr->xi, -imgptr->yi);
frontbuffer(1);
seed_fill(obj->x[0] - imgptr->xi, obj->y[0] - imgptr->yi, &dummyr,
Pack(ccol[0], ccol[1], ccol[2]));
frontbuffer(!double_buf);
}
static Draw_t *
make_fill(void)
{
static Draw_t dummy;
Draw_t *d = &dummy;
d->pen_init = fill_init;
d->pen_up = fill_up;
d->pen_term = fill_term;
d->builtin = 1;
return d;
}
/*********** END of paint definations *********** }*/
/**********************************************************************
* GUI part of paint function: ultimate goal is to get fill pattern,
* current color and linewidth
***********************************************************************/
static void
paint_cb(FL_OBJECT * ob, long q)
{
fl_freeze_form(ob->form);
/* flush last object */
if (pdraw->pen_term)
pdraw->pen_term(&cur_obj);
pdraw = (paintfunc + q)->how;
pen_move = pdraw->pen_move ? pdraw->pen_move : null_op;
if (pdraw->pen_init)
pdraw->pen_init(&cur_obj);
fl_unfreeze_form(ob->form);
}
static FL_OBJECT *lwbut;
static int get_line_width(void);
static int get_line_style(void);
/**********************************************************************
* Change line width:
* leftmouse ++, middlemouse -- and rightmouse randowm
************************************************************************/
/* ARGSUSED */
static void
linew_cb(FL_OBJECT * ob, long q)
{
char pp[10];
switch (fl_get_button_numb(ob))
{
case 1: /* right mouse */
/* warp mouse */
set_mouse(ob->form->x + ob->x, ob->form->y + ob->y);
cur_lw = get_line_width();
break;
case 2: /* middle mouse */
if (--cur_lw < 0)
cur_lw = MAX_LW;
break;
case 3: /* left mouse */
if (++cur_lw > MAX_LW)
cur_lw = 0;
break;
}
sprintf(pp, "%d", cur_lw);
fl_set_object_label(ob, pp);
fl_redraw_object(lwbut);
}
static int *whichc = ccol;
/* ARGSUSED */
static void
acolor_cb(FL_OBJECT * ob, long q)
{
whichc = (q ? lcol : ccol);
}
/* ARGSUSED */
static void
color_cb(FL_OBJECT * ob, long q)
{
get_color(imgptr, whichc, 1);
if (whichc == ccol)
op_mapcolor(OP_COL, ccol[0], ccol[1], ccol[2]);
}
void
set_fl_free_default(FL_OBJECT * ob)
{
ob->boxtype = FL_UP_BOX;
ob->lstyle = FL_NORMAL_STYLE;
ob->lsize = 10.0;
ob->lcol = FL_BLACK;
ob->align = FL_ALIGN_CENTER;
ob->col1 = FL_MAGIC1;
ob->col2 = FL_MAGIC2;
}
static Draw_t *
make_default(void)
{
static Draw_t defdraw;
Draw_t *d = &defdraw;
d->pen_motion = null_op;
d->pen_move = null_op;
d->pen_down = null_op;
d->pen_up = null_op;
return d;
}
/************************************************************************
* Select current pattern: cur_pat
*************************************************************************/
/*******************************************************************
* Show current selected pattern: unlike the pattern icons, here we
* truly show the pattern as GL would render it
*******************************************************************/
/* ARGSUSED */
static int
show_pat(FL_OBJECT * ob, int ev, float mx, float my, char key)
{
int x, y, w, h;
set_current_window(ob->form->window);
fl_drw_box(ob->boxtype, ob->x, ob->y, ob->w, ob->h, ob->col1, 2.0);
/*
* Even if fill pattern is none, we still want to show current drawing
* color
*/
setpattern(cur_pat == PAT_HOLLOW ? 0 : cur_pat);
cpack(Pack(ccol[0], ccol[1], ccol[2]));
w = 2 * PAT_W + 5;
h = PAT_H + 6;
x = ob->x + (ob->w - w) / 2;
y = ob->y + (ob->h - h) / 2 + 1;
rectf(x, y, x + w, y + h - 1);
setpattern(0);
if (cur_pat == PAT_HOLLOW)
{
fl_drw_text_beside(ob->align, ob->x, ob->y, ob->w, ob->h,
FL_WHITE, ob->lsize, ob->lstyle, "None");
fl_drw_text_beside(ob->align, ob->x + 1, ob->y - 1,
ob->w, ob->h, FL_BLACK, ob->lsize, ob->lstyle, "None");
}
return 0;
}
static Pattern_t patterns[] =
{
{PAT_SOLID, 32, pat_solid_bits},
{PAT_HOLLOW, 32, pat_hollow_bits},
{PAT_ICIRC, 32, pat_icirc2_bits},
{PAT_FCC, 32, pat_fcc_bits},
{PAT_RAN_CIRC, 32, pat_rcirc_bits},
{PAT_GRID16, 32, pat_grid16_bits},
{PAT_CIRC, 32, pat_circ2_bits},
{PAT_RHASH, 32, pat_rhash_bits},
{PAT_LHASH, 32, pat_lhash_bits},
{PAT_VHASH, 32, pat_vhash_bits},
{PAT_LBRICK, 32, pat_lbrick_bits},
{PAT_LFBRICK, 32, pat_lfbrick_bits},
{PAT_HHASH, 32, pat_hhash_bits},
{PAT_HVHASH, 32, pat_hvhash_bits},
{PAT_GRID_DOTS, 32, pat_griddots_bits},
{PAT_DN, 32, pat_dn_bits},
{PAT_ROUGH, 32, pat_rough_bits},
{PAT_CHECKER4, 32, pat_checker4_bits},
{PAT_CHESS, 32, pat_chess_bits},
{PAT_ICHESS, 32, pat_ichess_bits},
{PAT_GRID32, 32, pat_grid32_bits},
{PAT_GRID32X, 32, pat_grid32x_bits},
{PAT_DOTS, 32, pat_dots_bits},
{PAT_HEARTS, 32, pat_heart_bits},
{PAT_SQCTR, 32, pat_sqctr_bits},
{PAT_RAN_DOTS, 32, pat_rdots_bits}
};
static int npatterns = sizeof(patterns) / sizeof(patterns[0]);
static FL_OBJECT *pbut[10], *sld, *patgroup, *patbut;
static int shown, poffset;
static void
pattern_init(void)
{
Pattern_t *pat = patterns + 1;
static int patinit;
int i;
if (!patinit)
{
for (i = 1; i < npatterns; i++, pat++)
defpattern(pat->name, pat->size,
XBM_to_GL_pat(pat->size, pat->size, pat->mask));
patinit = 1;
}
}
/* ARGSUSED */
static void
set_pat(FL_OBJECT * ob, long q)
{
int n = q + poffset;
Pattern_t *pat = patterns;
if (n < npatterns)
{
pat = patterns + n;
cur_pat = pat->name;
fl_redraw_object(patbut);
}
}
/**********************************************************************
* Paging for pattern browser
*
**********************************************************************/
static void
refill_pattern(void)
{
int i, pi;
Pattern_t *pat;
fl_deactivate_form(fpaint);
fl_freeze_form(fpaint);
pat = patterns + poffset;
for (i = 0; i < shown && (pi = i + poffset) < npatterns; i++, pat++)
{
fl_set_active_bitmap(pbut[i], pi == cur_pat);
fl_set_bitmap_bitmap(pbut[i], pat->size, pat->size, pat->mask);
}
for (; i < shown; i++)
fl_set_bitmap_bitmap(pbut[i], 32, 32, pat_hollow_bits);
fl_unfreeze_form(fpaint);
fl_activate_form(fpaint);
}
/* ARGSUSED */
static void
up_cb(FL_OBJECT * ob, long q)
{
if (poffset > 0)
{
if ((poffset -= shown) < 0)
poffset = 0;
fl_set_slider_value(sld, 1.0 - (float) poffset / npatterns);
refill_pattern();
}
}
/* ARGSUSED */
static void
down_cb(FL_OBJECT * ob, long q)
{
int lastn;
if ((poffset + shown) < npatterns)
{
poffset += shown;
lastn = (poffset + shown);
lastn = (lastn > npatterns ? npatterns : lastn);
fl_set_slider_value(sld, 1.0 - (float) lastn / npatterns);
refill_pattern();
}
}
/* ARGSUSED */
static void
patslider_cb(FL_OBJECT * ob, long q)
{
int leftover = (npatterns % shown);
float sval = fl_get_slider_value(ob);
static int here;
if (here)
return;
here = 1;
poffset = (1.0 - sval) * (npatterns - leftover);
refill_pattern();
here = 0;
}
static void
draw_cur_line_obj(FL_OBJECT * ob)
{
int x, y;
set_current_window(ob->form->window);
fl_drw_box(ob->boxtype, ob->x, ob->y, ob->w, ob->h, ob->col1, 2.0);
if (cur_lw)
{
x = ob->x + 5;
y = ob->y + ob->h / 2;
set_line_attribute();
cpack(Pack(lcol[0], lcol[1], lcol[2]));
draw_line(x, y, x + ob->w - 10, y);
reset_line_attribute();
}
else
{
fl_drw_text_beside(ob->align, ob->x, ob->y, ob->w, ob->h,
ob->lcol, ob->lsize, ob->lstyle, "None");
}
}
/*****************************************************************
* Show current line attributes: width & style.
* Also handles style request
*****************************************************************/
/* ARGSUSED */
static int
show_lw(FL_OBJECT * ob, int ev, float mx, float my, char key)
{
int mb = key, n;
switch (ev)
{
case FL_DRAW:
draw_cur_line_obj(ob);
break;
case FL_PUSH:
if (mb == 3) /* leftmouse */
{
if (++cur_ls > total_ls)
cur_ls = 0;
}
else if (mb == 2)
{
if (--cur_ls < 0)
cur_ls = total_ls;
}
else
{
/* get_line_style coud return bad value */
cur_ls = (n = get_line_style()) >= 0 ? n : cur_ls;
}
fl_redraw_object(ob);
break;
}
return 0;
}
/* ARGSUSED */
static void
paint_done(FL_OBJECT * ob, long q)
{
fl_qenter(KEYBD, 27);
}
static void
read_pixel(int x, int y)
{
rgba_t c;
readsource(SRC_FRONT);
lrectread(x, y, x, y, &c);
readsource(SRC_AUTO);
Unpack(c, whichc[0], whichc[1], whichc[2]);
if (whichc == ccol)
{
op_mapcolor(OP_COL, ccol[0], ccol[1], ccol[2]);
fl_redraw_object(patbut);
}
else
fl_redraw_object(lwbut);
}
/* ARGSUSED */
static int
handle_cw(FL_OBJECT * ob, int ev, float mx, float my, char key)
{
int x, y, w, h, sep = 4;
int xi, yi;
w = ob->w / 2 - 2 * sep;
h = ob->h / 2 - 2 * sep;
x = ob->x + ob->w / 2;
y = ob->y + ob->h / 2;
xi = mx;
yi = my;
/* convert to obj based */
mx -= x;
my -= y;
if (!((mx * mx + my * my) <= (w * h)) && ev != FL_DRAW)
return 0;
set_current_window(ob->form->window);
reshapeviewport();
switch (ev)
{
case FL_DRAW:
fl_drw_box(ob->boxtype, ob->x, ob->y, ob->w, ob->h, ob->col1, 2.0);
draw_color_wheel(x, y, w, h);
break;
case FL_PUSH:
case FL_RELEASE:
case FL_MOUSE:
read_pixel(xi, yi);
return 0;
}
return 0;
}
/* ARGSUSED */
static void
undo_update_cb(FL_OBJECT * ob, long q)
{
(q ? paint_update : paint_undo) ();
}
/* ARGSUSED */
static void
create_cb(FL_OBJECT * ob, long q)
{
int w = 800, h = 600, white = 1;
int status;
status = get2int("Width", &w, 100, 1000, "Height", &h, 100, 1000,
"White", &white, -1, 0);
if (status < 0) /* cancel */
return;
free_image(saved);
free_image(imgptr);
saved = imgptr = 0;
imgptr = create_image("", T_RGBA, w, h, 0xffffff * white);
saved = img_dup(imgptr);
imgptr->io->display(imgptr, 0, 1);
changed = 0;
}
static FL_FORM *
create_form_fpaint(void)
{
FL_OBJECT *obj, *aobj = 0;
int i, k;
float x, y, dx, dy;
PaintFunc *pf = paintfunc, *pfs = pf + npaintfunc;
float fw = 400, fh = 335.0;
Pattern_t *pat = patterns;
if (fpaint)
return fpaint;
fpaint = fl_bgn_form(FL_NO_BOX, fw, fh);
obj = fl_add_box(FL_UP_BOX, 0.0, 0.0, fw, fh, "");
fl_set_object_color(obj, FL_SLATEBLUE, 0);
obj = fl_add_button(FL_HB, 0.0, 0.0, fw, fh, "");
fl_set_call_back(obj, help_cb, HELP_PAINT);
dx = 80.0;
dy = 25.0;
x = fw - dx - 10;
y = 10.0;
obj = fl_add_button(FL_NORMAL_BUTTON, x, y, dx, dy, "Done");
fl_set_call_back(obj, paint_done, 0);
fl_set_object_lsize(obj, 10.0);
y += dy;
obj = fl_add_button(FL_NORMAL_BUTTON, x, y, dx, dy, "Color");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, color_cb, 0);
y += dy;
obj = fl_add_button(FL_NORMAL_BUTTON, x, y, dx, dy, "Update");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, undo_update_cb, 1);
y += dy;
obj = fl_add_button(FL_NORMAL_BUTTON, x, y, dx, dy, "Undo");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, undo_update_cb, 0);
y += dy;
obj = fl_add_button(FL_NORMAL_BUTTON, x, y, dx, dy, "Create");
fl_set_object_lsize(obj, 10.0);
fl_set_call_back(obj, create_cb, 0);
y += dy;
/* paint function icon group */
fl_bgn_group();
x = 10;
dx = fw - 2 * x;
dy = 105;
y = fh - dy - 10;
obj = fl_add_box(FL_DOWN_BOX, x, y, dx, dy, "");
fl_set_object_color(obj, FL_SLATEBLUE, 0);
dx = dy = 46.0;
x = 15.0;
y = 220 + 105 - dy - 5;
for (i = 0; pf < pfs; i++, pf++, x += dx)
{
pf->how = (pf->make ? pf->make : make_default) ();
if (x > (430 - dx - 4))
{
y -= dy + 1;
x = 15.0;
}
obj = pf->bitmap ?
fl_add_active_bitmap(FL_RADIO_BITMAP, x, y, dx, dy, "") :
fl_add_icon(FL_RADIO_ICON, x, y, dx, dy, "");
if (i == 0)
aobj = obj;
fl_set_object_boxtype(obj, FL_UP_BOX);
if (pf->bitmap)
fl_set_bitmap_bitmap(obj, BITMAP_W, BITMAP_H, pf->icons);
else
fl_set_icon_file(obj, get_HELPFile(pf->icons));
fl_set_call_back(obj, paint_cb, i);
}
pf = paintfunc;
/** Set default operations */
(pf->bitmap ? fl_set_active_bitmap : fl_set_icon) (aobj, 1);
pdraw = pf->how;
pen_move = pdraw->pen_move ? pdraw->pen_move : null_op;
fl_end_group();
/* color cube */
dx = 180;
x = 120;
dy = 180;
y = 170 + 45 - dy;
obj = fl_add_free(FL_NORMAL_FREE, x, y, dx, dy, "", handle_cw);
set_fl_free_default(obj);
fl_set_object_boxtype(obj, FL_DOWN_BOX);
fl_set_object_color(obj, FL_SLATEBLUE, 0);
dx /= 2;
dy = 25;
fl_bgn_group();
obj = fl_add_lightbutton(FL_RB, x, 10.0, dx, dy, "OutlnCol");
fl_set_call_back(obj, acolor_cb, 1);
fl_set_object_lsize(obj, 10.0);
obj = fl_add_lightbutton(FL_RB, x + dx + 1, 10.0, dx, dy, "DrawCol");
fl_set_call_back(obj, acolor_cb, 0);
fl_set_object_lsize(obj, 10.0);
fl_set_button(obj, 1);
acolor_cb(obj, 0);
fl_end_group();
/**** Handle line width ***************************************/
obj = fl_add_box(FL_DOWN_BOX, 10.0, 175.0, 100.0, 40.0, "");
fl_bgn_group();
obj = fl_add_button(FL_NB, 15.0, 180.0, 25.0, 30.0, "1");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_lsize(obj, FL_SMALL_FONT);
fl_set_call_back(obj, linew_cb, 0);
lwbut = obj = fl_add_free(FL_NORMAL_FREE, 40.0, 180.0, 65.0, 30.0, "",
show_lw);
set_fl_free_default(obj);
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_object_color(obj, FL_GRAY91, 0);
fl_end_group();
/***** All patterns *****************************************/
obj = fl_add_box(FL_DOWN_BOX, 10.0, 10.0, 100.0, 157.0, "");
patbut = obj = fl_add_free(FL_SLEEPING_FREE, 12.0, 125.0, 96.0, 50.0,
"", show_pat);
set_fl_free_default(obj);
fl_set_object_boxtype(obj, FL_DOWN_BOX);
fl_set_object_color(obj, FL_GRAY92, FL_MAGIC1);
pattern_init();
patgroup = fl_bgn_group();
x = 14.0;
y = 85.0;
dx = dy = 34.0;
for (k = 0; k < npatterns && y >= 10; x = 15.0, y -= dy + 1)
{
pbut[k] = obj = fl_add_active_bitmap(FL_RADIO_BITMAP,
x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_BORDER_BOX);
fl_set_object_color(obj, FL_INACTIVE, FL_GRAY91);
fl_set_bitmap_bitmap(obj, pat->size, pat->size, pat->mask);
fl_set_active_bitmap_color(obj, FL_BLACK);
fl_set_call_back(obj, set_pat, k);
k++;
pat++;
x += dx + 1;
pbut[k] = obj = fl_add_active_bitmap(FL_RADIO_BITMAP,
x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_BORDER_BOX);
fl_set_object_color(obj, FL_INACTIVE, FL_GRAY91);
fl_set_active_bitmap_color(obj, FL_BLACK);
fl_set_bitmap_bitmap(obj, pat->size, pat->size, pat->mask);
fl_set_call_back(obj, set_pat, k);
k++;
pat++;
}
fl_set_active_bitmap(pbut[0], 1);
fl_end_group();
shown = k;
obj = fl_add_button(FL_NORMAL_BUTTON, 85.0, 100.0, 20.0, 20.0, "@8");
fl_set_object_lcol(obj, FL_RED);
fl_set_call_back(obj, up_cb, 0);
obj = fl_add_button(FL_NORMAL_BUTTON, 85.0, 15.0, 20.0, 20.0, "@2");
fl_set_object_lcol(obj, 1);
fl_set_call_back(obj, down_cb, 0);
sld = obj = fl_add_slider(FL_VERT_SLIDER, 85.0, 35.0, 20.0, 65.0, "");
fl_set_object_boxtype(obj, FL_FRAME_BOX);
fl_set_slider_bounds(obj, 0.0, 1.0);
fl_set_slider_value(obj, 1.0);
fl_set_object_color(obj, FL_MAGIC1, FL_RED);
fl_set_slider_size(obj, 0.20);
fl_set_call_back(obj, patslider_cb, 0);
fl_end_form();
fl_set_form_position(fpaint, -1, -1);
return fpaint;
}
/*********************************************************************
* GUI hack get line width
*********************************************************************/
#define LW_BW 40 /* bitmap width for line_width icon */
#define MAX_PREDEF 12 /* max. predefined line widths */
static char bitmaps[LW_BW * MAX_PREDEF + 1];
static FL_FORM *create_form_lw(void);
#include "bitmaps/q.xbm"
static int lw;
static void
line_width_init(void)
{
if (bitmaps[0] == 0)
memset(bitmaps, 0xff, sizeof(bitmaps));
}
static int
get_line_width(void)
{
short val;
line_width_init();
fl_deactivate_all_forms();
bit_show_form(create_form_lw(), FL_PLACE_HOTSPOT, 0, "");
while (bit_qread(&val) != KEYBD && val != 27)
;
bit_hide_form(create_form_lw());
fl_activate_all_forms();
return lw;
}
/* ARGSUSED */
static void
lw_cb(FL_OBJECT * ob, long q)
{
lw = q;
fl_qenter(KEYBD, 27);
}
/* ARGSUSED */
static void
input_cb(FL_OBJECT * ob, long q)
{
getint("LineWidth", &lw, 0, MAX_LW, 0);
fl_qenter(KEYBD, 27);
}
static FL_FORM *
create_form_lw(void)
{
FL_OBJECT *obj;
float w = 90.0, h = 170.0;
float x, y, dx, dy;
char pp[20];
int i;
static FL_FORM *fmlw;
if (!fmlw)
{
fmlw = fl_bgn_form(FL_SHADOW_BOX, w, h);
x = 10.0;
dy = 20;
dx = 70.0;
y = h - dy - 5;
fl_bgn_group();
obj = fl_add_active_bitmap(FL_RADIO_BITMAP, x, y, dx, dy, "");
fl_set_object_boxtype(obj, FL_BORDER_BOX);
fl_set_bitmap_bitmap(obj, 12, 12, q_bits);
fl_set_call_back(obj, input_cb, 0);
y -= dy + 1;
dy = MAX_PREDEF;
x = 2;
dx = 22.0;
for (i = 0; i < MAX_PREDEF && y >= 5; i++, y -= dy, x = 2)
{
obj = fl_add_text(FL_NORMAL_TEXT, x, y, dx, dy, "");
sprintf(pp, "%d", i);
fl_set_object_lsize(obj, FL_SMALL_FONT);
fl_set_object_align(obj, FL_ALIGN_RIGHT);
fl_set_object_label(obj, pp);
x += dx + 1;
obj = fl_add_active_bitmap(FL_RADIO_BITMAP,
x, y, 50.0, dy, i ? "" : "None");
fl_set_object_boxtype(obj, FL_NO_BOX);
fl_set_bitmap_bitmap(obj, LW_BW, i, bitmaps);
fl_set_call_back(obj, lw_cb, i);
}
fl_end_group();
fl_end_form();
fl_set_form_hotspot(fmlw, 1, h - 1);
}
return fmlw;
}
/**********************************************************************
* Get line style;
**********************************************************************/
typedef struct
{
char *name;
Linestyle ls;
}
LS_t;
static LS_t ls_def[] =
{
{"Solid", 0xffff},
{"Dotted", 0x3333},
{"DashedS", 0x3f3f},
{"DashedL", 0xff00},
{"LinePoint", 0xf18f}
};
static long lsmenu = -1;
static int
init_line_style(void)
{
int i;
static int nls = sizeof(ls_def) / sizeof(ls_def[0]);
if (lsmenu < 0)
{
lsmenu = defpup("LineStyle%t|Solid");
for (i = 1; i < nls; i++)
{
deflinestyle(i, ls_def[i].ls);
addtopup(lsmenu, ls_def[i].name, 0);
}
}
return nls - 1;
}
static int
get_line_style(void)
{
init_line_style();
return dopup(lsmenu) - 1;
}